/******************************************************************************
 * $Id: funcmisc.c,v 1.11 2004/09/22 01:14:22 jimb Exp $
 * @file
 * @author	Jim Brooks http://www.jimbrooks.org
 * @brief	Misc. functions related to function call graph and function tags.
 ******************************************************************************/

#include "common.h"
#include "funcmisc.h"

#include "module.h"
#include "text.h"

/*****************************************************************************
 * Find a funcDef_t struct by a pointer to its tag_t.
 *****************************************************************************/
funcDef_t*
FindFuncDefByItsTag( tag_t* pTagFuncDefSought )
{
   GList*		pLinkModuleName		= NULL;
   funcDef_t*	pFuncDef			= NULL;
   char*        pActiveModuleName   = NULL;

g_return_val_if_fail( pTagFuncDefSought, NULL );

  /*
   * For an optimal match, first search the current module.
   */
   pActiveModuleName = ActiveModuleName();
   if ( pActiveModuleName  &&  pActiveModuleName[0] )
   {
      pFuncDef = FindFuncDefByItsTagInOneModule( pTagFuncDefSought,
                                                 pActiveModuleName );
      if ( pFuncDef ) return pFuncDef;
   }

  /*
   * Search each module's list of funcDef_t.
   */
   for ( pLinkModuleName = pGlistModuleNames;
         pLinkModuleName;
         pLinkModuleName = pLinkModuleName->next )
   {
      pFuncDef = FindFuncDefByItsTagInOneModule( pTagFuncDefSought,
                                                 (gchar*) pLinkModuleName->data );

      if ( pFuncDef ) return pFuncDef;
   }

   return NULL;
}

/*****************************************************************************
 * Searching in one module, find a funcDef_t struct by a pointer to its tag_t.
 * @param	pTagFuncDefSought
 * @param	pModuleNameToSearch
 * @param	pHashModules (GLOBAL)
 * @return	Pointer to a funcDef_t.
 *****************************************************************************/
funcDef_t*
FindFuncDefByItsTagInOneModule( tag_t* pTagFuncDefSought,
                                gchar* pModuleNameToSearch )
{
   module_t*	pModule			= NULL;
   GSList*		pFuncDefList	= NULL;
   funcDef_t*	pFuncDef		= NULL;

g_return_val_if_fail( pTagFuncDefSought  &&  !IsStringEmpty(pModuleNameToSearch), NULL );

   pModule = g_hash_table_lookup( pHashModules, pModuleNameToSearch );
g_return_val_if_fail( pModule, NULL );

  /*
   * For each funcDef_t in this module (for each in module_t.pGslistFuncDef).
   */
   for ( pFuncDefList = pModule->pGslistFuncDef;
         pFuncDefList;
         pFuncDefList = pFuncDefList->next )
   {
     /*
      * Does the tag pointer in funcDef_t match?
      */
      pFuncDef = (funcDef_t*) pFuncDefList->data;

      if ( pFuncDef  &&  pFuncDef->pTagFuncDef == pTagFuncDefSought )
         return pFuncDef;
   }

   return NULL;
}

/*****************************************************************************
 * Search the list of function definitions to determine if the line
 * the cursor is on is within a function definition.
 * @param	line
 *			Line # of text cursor.
 * @return	Pointer to a funcDef_t struct.
 *****************************************************************************/
funcDef_t*
SearchFuncDefList( int line )
{
   GSList*		pGslistFuncDef;
   int			lineStart;
   int			lineLast;

#ifdef HYPERSRC_PEDANTIC
g_return_val_if_fail( ActiveModule(), NULL );
#endif

  /*
   * For each funcDef struct in the list.
   */
   for ( pGslistFuncDef = ActiveModule()->pGslistFuncDef;
         pGslistFuncDef;
         pGslistFuncDef = pGslistFuncDef->next )
   {
      funcDef_t*	pFuncDef = pGslistFuncDef->data; /* pointer to funcDef per se */
#ifdef HYPERSRC_PEDANTIC
g_return_val_if_fail( pFuncDef, NULL );
#endif

      lineStart = pFuncDef->lineStart;
      lineLast  = pFuncDef->lineLast;

#ifdef HYPERSRC_PEDANTIC
      if ( ! ( lineStart <= lineLast  &&  lineStart >=0  &&  lineLast >= 0  ) )
      {
         DebugMsg( "SearchFuncDefList() line checks failed in module %s func def %s",
                   ActiveModule()->pModuleName,
                   pFuncDef->pTagFuncDef->pName );
         DebugMsg( "  lineStart = %d  lineLast %d ", lineStart, lineLast );
         continue;
      }
#endif

      if ( line >= lineStart  &&  line <= lineLast )
         return pFuncDef;
   }

   return NULL;
}

/*****************************************************************************
 * @return TRUE if the module that contains a function is known.
 *****************************************************************************/
gboolean
IsModuleOfFuncKnown( tag_t* pTagFunc )
{
#ifdef HYPERSRC_PEDANTIC
g_return_val_if_fail( pTagFunc, TRUE );
#endif
  /*
   * If AddFuncCallToGraph() parses a function call but the caller itself
   * wasn't tagged, then the tag's type will be "call".
   */
   return STRNE( pTagFunc->pType, "call" );
}

/*****************************************************************************
 * Filters out later duplicate calls made by the same func def.
 * @param	FuncCallList
 *          List of funcCall_t structs.  This list belongs to one func def.
 * @param	pFuncCall
 *			Must point to a link in pFuncCallList.
 * @return	Returns true if this func call should be ignored.
 *          Returns false if no duplicate calls precedes the referenced one.
 *****************************************************************************/
gboolean
FilterDuplicateFuncCalls( GSList*     pFuncCallList,
                          funcCall_t* pFuncCall )
{
#ifdef HYPERSRC_PEDANTIC
g_return_val_if_fail( pFuncCallList  &&  pFuncCall, FALSE );
#endif

  /*
   * Only return false for the first duplicated call (there will be at least one).
   */
   for ( ;
         pFuncCallList;
         pFuncCallList = pFuncCallList->next )
   {
      if ( ((funcCall_t*)pFuncCallList->data)->pTagFuncCall == pFuncCall->pTagFuncCall )
         return pFuncCallList->data != pFuncCall;
   }

  /*
   * Shouldn't reach here.
   */
g_return_val_if_fail(0,FALSE);
}
